feat: worktree-native workspace for parallel branch work#22
Merged
Conversation
Add four new abilities — workspace-worktree-add|list|remove|prune — and a matching "workspace worktree" CLI subcommand. The workspace now stores each branch in its own directory at <workspace>/<repo>@<branch-slug>, so multiple agent sessions can edit different branches of the same repo simultaneously without stepping on each other. All existing read/write/git abilities accept the new <repo>@<branch-slug> handle alongside bare repo names; resolution happens through Workspace:: parse_handle() and a single resolve_repo_path() call site. Mutating ops on the primary checkout (bare <repo>) now require allow_primary_mutation=true / --allow-primary-mutation. The default-deny codifies the "primary tracks the deployed branch, hands off" rule that lived as informal discipline before. Worktrees are always allowed. clone rejects @-suffixed names; remove refuses to delete a primary that has linked worktrees attached; git push only enforces fixed_branch on the primary, freeing worktrees to push any branch. Includes pure-PHP smoke test for handle parsing/slugify (tests/ smoke-worktree-handles.php, 32/32 passing) and a manual end-to-end test plan (tests/TESTING.md). AGENTS.md generator and README updated to teach the worktree-first workflow.
Found and fixed during end-to-end testing on the intelligence-chubes4 site (DATAMACHINE_WORKSPACE_PATH=~/Developer): - Ability callbacks now declare `array|WP_Error` return types so the primary-mutation guard (and other WP_Error returns) propagate cleanly instead of throwing a TypeError in WorkspaceAbilities::*. - Add `--allow-primary-mutation` to the `workspace git` CLI docblock so WP-CLI accepts the flag. - `workspace list` now surfaces `kind` (primary|worktree) and `repo` columns so the new on-disk model is visible. - `workspace-worktree-list` allows `branch_slug` and `branch` to be null in its output schema (primaries have no slug; detached heads have no branch). - New `external` field on worktree-list output. Worktrees outside the workspace path (e.g. /private/tmp/dm-worktrees/post-tracking from a manual `git worktree add`) now report their absolute path as the handle instead of a mangled relative slice. - New `Workspace::resolve_default_base()` helper. `worktree add` defaults to `origin/HEAD` when --from is omitted (per design), with a safe fallback to plain HEAD if origin/HEAD is unset. Live test summary on intelligence-chubes4: - ✓ workspace list shows primary/worktree split - ✓ worktree add data-machine-code test/smoke (created) - ✓ worktree list discovers both in-workspace and external worktrees - ✓ git status data-machine-code@test-smoke - ✓ primary mutation guard fires (blocked by existing write_enabled policy first; both gates work) - ✓ clone --name=foo@bar rejected (`@` reserved for worktrees) - ✓ worktree remove cleans the directory and prunes the registry - ✓ end-to-end on mcp-context-wporg with explicit --from=origin/main Note: workspace read/write/edit/ls still hit the pre-existing Studio WASM mount limitation when paths are outside the site folder. Tracked elsewhere; unrelated to this PR.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Make the agent workspace worktree-native so multiple parallel sessions can cook features in the same repo without stepping on each other.
Today every session shares one git checkout per repo at
~/.datamachine/workspace/<repo>/. Two sessions branch-switching at the same time corrupts each other's work — the classic "agent stepped on itself" pattern.After this change, each branch lives in its own directory:
```
~/.datamachine/workspace/
├── data-machine-code/ ← primary, hands-off by default
├── data-machine-code@fix-foo/ ← worktree for fix/foo
└── data-machine-code@feat-bar/ ← worktree for feat/bar
```
The
<repo>@<branch-slug>naming makes the on-disk directory the handle. All existing read/write/git abilities transparently accept either form. No new flag plumbing through every ability.What's in here
4 new abilities (CLI-only by default;
listis REST-readable):datamachine/workspace-worktree-add—{repo, branch, from?}→ creates<repo>@<slug>as a git worktree, checks outbranch(creating offfromif it does not exist locally; defaultorigin/HEAD).datamachine/workspace-worktree-list—{repo?}→ branches, heads, dirty counts. Distinguishes primary vs worktree.datamachine/workspace-worktree-remove—{repo, branch, force?}→ refuses if dirty unless forced.datamachine/workspace-worktree-prune— runsgit worktree pruneacross all primaries.1 new CLI subcommand:
```
wp datamachine-code workspace worktree add [--from=]
wp datamachine-code workspace worktree list [] [--format=json]
wp datamachine-code workspace worktree remove [--force]
wp datamachine-code workspace worktree prune
```
Handle resolver —
Workspace::parse_handle()splits<repo>@<slug>into components. Called byWorkspace::get_repo_path()andresolve_repo_path(), soWorkspaceReaderandWorkspaceWriterwork on either primary or worktree handles with zero call-site changes.Branch slug —
/becomes-(fix/foo-bar→fix-foo-bar). Anything outside[A-Za-z0-9._-]is stripped; runs of dashes collapse.Primary read-only by default — mutating ops (
git pull|add|commit|push) on a bare<repo>handle now require--allow-primary-mutation(CLI) /allow_primary_mutation: true(ability input). Codifies the discipline that the primary checkout tracks the deployed branch and shouldn't be touched by parallel agents.Policy updates:
clonerejects@-suffixed names — that suffix is reserved for worktrees.removerefuses to delete a primary that has linked worktrees attached (with helpful error listing them).git pushonly enforcesfixed_branchon the primary checkout. Worktrees may push any branch — that's the whole point of having them.Tests
tests/smoke-worktree-handles.php— pure-PHP unit smoke forparse_handle()andslugify_branch(). No WP bootstrap required. 32/32 passing.tests/TESTING.md— manual end-to-end checklist: clone → worktree add → edit → commit → push → remove, plus all the negative cases (dirty refuse, primary mutation guard, clone-with-@, primary-with-linked-worktrees).Backward compatibility
All bare
<repo>handles continue to resolve to the primary checkout. The only behavioral break is the new primary mutation guard — existing automation that mutates the primary needs--allow-primary-mutation. That's the intended default-deny.Why this matters
Today, parallel agent work in the same repo is unsafe. After this lands, every Discord thread / kimaki session can have its own worktree handle for the repo it's editing. Multiple features cooked at once, isolated on disk, reviewed as separate PRs. Unblocks the autonomous-parallel-agent story end-to-end.
Design doc: see the "Workspace Worktrees Design" wiki article in the linked Intelligence site.